package io.cici.cc.mybatis.plus.core;

import io.cici.cc.mybatis.lite.binding.BindingException;
import io.cici.cc.mybatis.lite.binding.MapperRegistry;
import io.cici.cc.mybatis.lite.session.Configuration;
import io.cici.cc.mybatis.lite.session.Session;
import io.cici.cc.mybatis.plus.core.override.MybatisMapperProxyFactory;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class MybatisMapperRegistry extends MapperRegistry {

    private final Configuration config;

    private final Map<Class<?>, MybatisMapperProxyFactory<?>> map = new ConcurrentHashMap<>();

    public MybatisMapperRegistry(Configuration config) {
        super();
        this.config = config;
    }


    @Override
    public <T> T getMapper(Class<T> type, Session session) {

        MybatisMapperProxyFactory<T> mybatisMapperProxyFactory = (MybatisMapperProxyFactory<T>) map.get(type);
        if (mybatisMapperProxyFactory == null) {
            mybatisMapperProxyFactory = (MybatisMapperProxyFactory<T>) map.entrySet().stream()
                    .filter(t -> t.getKey().getName().equals(type.getName())).findFirst().map(Map.Entry::getValue)
                    .orElseThrow(() -> new BindingException(
                            "Type " + type + " is not known to the MybatisPlusMapperRegistry."));
        }
        try {
            return mybatisMapperProxyFactory.newInstance(session);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

    @Override
    public <T> boolean hasMapper(Class<T> type) {
        return map.containsKey(type);
    }

    /**
     * 清空 Mapper 缓存信息
     */
    protected <T> void removeMapper(Class<T> type) {
        map.entrySet().stream().filter(t -> t.getKey().getName().equals(type.getName())).findFirst()
                .ifPresent(t -> map.remove(t.getKey()));
    }

    @Override
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                // TODO 如果之前注入 直接返回
                return;
                // TODO 这里就不抛异常了
//                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                // TODO 这里也换成 MybatisMapperProxyFactory 而不是 MapperProxyFactory
                map.put(type, new MybatisMapperProxyFactory<>(type));
                // It's important that the type is added before the parser is run
                // otherwise the binding may automatically be attempted by the
                // mapper parser. If the type is already known, it won't try.
                // TODO 这里也换成 MybatisMapperAnnotationBuilder 而不是 MapperAnnotationBuilder
                MybatisMapperAnnotationBuilder parser = new MybatisMapperAnnotationBuilder(config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    map.remove(type);
                }
            }
        }
    }

    /**
     * 使用自己的 knownMappers
     */
    @Override
    public Collection<Class<?>> getMappers() {
        return Collections.unmodifiableCollection(map.keySet());
    }

}
